#coding=utf-8
#
# Copyright (C) 2014  NianNian TECH Co., Ltd. All rights reserved.
# Created on Apr 21, 2014, by Junn
# 
#
from django.contrib.auth import logout as system_logout 
from utils.http import standard_response, JResponse
from core.views import CustomAPIView
from users.forms import UserSignupForm, UserLoginForm, PasswordChangeForm,\
    PasswordResetForm
from core import codes
from users.models import User
from utils import http, logs, eggs
from auth.models import CodeRecord, OpenAccount
from core.decorators import login_required_mtd
from families.models import Family
from core.serializers import serialize_response
from rest_framework.decorators import api_view
import settings
import time
from django.db import transaction


#@post_request_required
@api_view(['POST'])
def verify_phone(req):
    '''验证手机号合法性: 格式是否正确, 是否已注册, 若格式正确且未注册则发送短信验证码'''
    
    ## 临时代码, 防止非法短信请求
    scode = req.REQUEST.get('scode')
    if not scode == settings.SECRET_KEY:
        return http.resp('permission_denied')
    
    action = req.REQUEST.get('action')
    if action not in ('signup', 'bind', 'reset'):
        logs.error(u'无效的操作类型: %s, action必须是: signup/bind/reset' % action)
        return http.failed('无效的操作类型')
    
    # 检查手机号格式
    phone = req.REQUEST.get('phone')
    phone = phone.strip()
    if not eggs.is_phone_valid(phone):
        return http.failed('请输入有效的手机号')
    
    # 排除异常情况
    user = User.objects.get_by_phone(phone)
    if user and action == 'signup':
        logs.debug('该手机号已注册')
        return http.failed(u'该手机号已注册')
    
    if not user and action == 'reset':
        logs.debug('该手机号未注册, 无法进行重置操作')
        return http.failed('该手机号尚未注册')
        
    if user and action == 'bind':
        logs.debug('该手机号已被绑定')
        return http.failed(u'该手机号已被绑定')
    
    success, result = CodeRecord.objects.gen_code(phone, action=action, re_gen=True)
    if not success and result == -1:
        return http.failed('验证码获取次数超过当天限制')
        
    if success:
        return http.ok({'vcode': result})
    
    return http.failed('验证码获取异常')
    
    
#@post_request_required
@api_view(['POST'])
def check_sms_code(req):
    '''检查短信验证码是否合法, 所有验证码的有效期均为10分钟'''
    
    phone = req.POST.get('phone')
    vcode = req.POST.get('vcode')
    action = req.POST.get('action')
    
    return http.ok() if _is_sms_code_valid(phone, vcode, action) else http.failed('无效的短信验证码')

def _is_sms_code_valid(phone, vcode, action):
    return CodeRecord.objects.check_code(phone, vcode, action=action)
    
SIGNUP_AFTER_LOGIN = True  #默认注册后自动登录

#@post_request_required
@api_view(['POST'])
def signup1(req):
    '''检查验证码及注册账号生成仅一次请求完成, username即phone'''
    
    username = req.POST.get('username')
    vcode = req.POST.get('vcode')
    if not username or not vcode:
        return http.failed('手机号或验证码为空')
    
    if settings.SMS_CODE_REQUIRED and not _is_sms_code_valid(username, vcode, 'signup'):
        return http.failed('无效的短信验证码')
    
    form = UserSignupForm(data=req.POST)
    if form.is_valid():
        user = form.save()
        family = Family.objects.create_default(user)
        
        if SIGNUP_AFTER_LOGIN:
            user.post_login(req)
            
            # 注册并登录后, 仅返回uid及fid, 用户将进入第3步填写完善的个人资料
            response = http.ok({'uid': user.id, 'fid': family.id})
            
            token = user.get_authtoken()
            if not token: return http.resp('authtoken_error')
            response.set_cookie('authtoken', token)
            return response
            
        return http.ok()
    else:
        if form.error_status == 'invited_signup':
            user = User.objects.get_by_phone(username)
            try:
                return http.failed('用户%s已将您添加到Ta的亲友圈, 请用收到的短信密码直接登录. 若未收到短信或忘记密码, 请直接用手机号找回密码' % user.get_family.creator.username)
            except Exception, e:
                logs.error('error occurred when signup for invited family user \n %s' % e)
                return http.failed('操作异常: 隐式注册手机号已注册')
            
        elif form.error_status == 'duplicate_username':
            return http.failed('该手机号已注册')
        elif form.error_status == 'pwd_format_error':
            return http.failed('密码只能为6-16位英文字符或下划线组合')
        else:
            logs.info('errors when signup: \n %s' % form.errors)
            return http.resp('form_errors', ':%s' % form.errors)
    

class SignupAction(CustomAPIView):
    
    def get(self, req):
        return http.http404()
        #return standard_response('test.html',{'form': UserSignupForm}, req)
    
    def post(self, req):
        '''TODO: 后续应添加: 手机号已被验证逻辑'''
        
        form = UserSignupForm(data=req.POST)
        if form.is_valid():
            user = form.save()
            family = Family.objects.create_default(user)
            return http.ok({'uid': user.id, 'fid': family.id})
        else:
            ctx = {}
            ctx['errors'] = form.errors
            ctx.update(codes.get('form_errors'))
            return JResponse(ctx)
    
class LoginAction(CustomAPIView):

    def get(self, req):
        # TODO: if is login, need to redirect here
        return standard_response('test.html',{'form': UserLoginForm}, req)

    def post(self, req):
        form = UserLoginForm(req, data=req.POST)
        if form.is_valid():
            user = form.login(req)
            family = user.get_family
            if not family: # 不返回用户与家庭相关的信息
                response = serialize_response(user)
            else:    
                response = serialize_response(family.get_member(user.id))
            
            token = user.get_authtoken()
            if not token:
                return http.resp('authtoken_error', 'Auth error')
            
            response.set_cookie('authtoken', token)
            return response
        else:
            if form.error_status == 'passwd_set_required':  #被邀请用户首次登录, 或用户未设置密码
                return http.resp('passwd_set_required')
            
            logs.error('form error: \n %s' % form.errors)
            if form.error_status == 'inactive':
                return http.failed('您的帐号已暂停使用')
            
            return http.failed('用户名或密码错误')
            

@api_view(['POST'])        
def logout(req):
    if req.user.is_authenticated():
        system_logout(req)
        return http.ok()
    return http.failed('退出失败')

@api_view(['POST'])
@login_required_mtd
def change_password(req):
    if not req.POST.get('password') or not req.POST.get('password1'):
        return http.failed('密码为空或格式不正确')
    
    form = PasswordChangeForm(req.user, data=req.POST)
    if form.is_valid():
        form.set_password()
        return http.ok()

    return http.failed('密码错误')

@api_view(['POST'])
def reset_password(req):
    '''若为手机号登录, 进入该函数前需先获取验证码'''
    
    phone = req.REQUEST.get('phone')
    vcode = req.REQUEST.get('vcode') #手机验证码
    if not _is_sms_code_valid(phone, vcode, action='reset'):
        return http.failed('短信验证码无效')
    
    try:
        user = User.objects.get(username=phone.strip())
    except User.DoesNotExist:
        logs.info('User not exist: %s' % phone)
        return http.object_not_found()
        
    form = PasswordResetForm(user, data=req.POST)
    if form.is_valid():
        form.reset()
        return http.ok()
    return http.failed('两次输入的密码不匹配')
    
@api_view(['POST'])
@login_required_mtd
def bind_phone(req):
    '''绑定手机号.
    适用于非手机号登录(如第3方登录)时, 将手机号与用户当前登录账号绑定. 
    绑定后, 手机号可用于登录或重置密码. 
    '''
    
    phone = req.REQUEST.get('phone', '')
    if not eggs.is_phone_valid(phone):
        return http.failed('请输入有效的手机号')
    
    vcode = req.REQUEST.get('vcode') #手机验证码
    phone = eggs.normalize_phone(phone)
    if not _is_sms_code_valid(phone, vcode, action='bind'):
        return http.failed('短信验证码无效')
    
    
    user = User.objects.get_by_phone(phone)
    if user:
        return http.failed('手机号已注册或已被绑定')
    
    user = req.user
    user.username = phone
    user.phone = phone
    user.save()
    user.cache()
    return http.ok()    

class OpenLogin(CustomAPIView):
     
    def post(self, req):
        '''
        处理第3方登录. 当客户端请求第3方服务器登录成功后, 请求咚咚服务器进行登录处理.
        '''
         
        source_site = req.POST.get('source_site', '')
        openid = req.POST.get('openid', '')
        access_token = req.POST.get('access_token', '')
        if not source_site or not openid or not access_token:
            logs.error(u'缺少必要的第3方登录参数: source_site %s, openid %s, access_token %s' % (source_site, openid, access_token))
            return http.failed('Uncompleted The3rd-login params')
        
        #refresh_token = req.POST.get('refresh_token', '')
        expires_in = req.POST.get('expires_in', int(time.time() + 7689600)) # 三个月后的unix秒数(按89天)
         
        try:
            open_acct = OpenAccount.objects.get(openid=openid, source_site=source_site)  #TODO: access_token是否需要验证? 
            user = open_acct.user
            #user = User.objects.get_cached(open_acct.user_id)
        except OpenAccount.DoesNotExist:
            open_name = req.POST.get('open_name', '')
            avatar_url = req.POST.get('avatar_url', '')
            
            ############## 对新账号进行注册并绑定处理 #############
            try:    
                user = User.objects._create_user( #第3方平台注册用户不允许直接登录, 除非重置了密码(重置密码需要先绑定手机号)
                    username=eggs.gen_uuid1(), nickname=open_name, acct_type='O'
                )
                
                try:
                    user.save_avatar(http.request_file(avatar_url))  #请求远端获取图片并保存
                except Exception, e:
                    logs.err(__name__, eggs.lineno(), e)
                
                user.updatePdu(1)  #设置头像标识位
                user.updatePdu(2)  #设置昵称标识位
                user.save()
                user.cache()
                
                open_acct = OpenAccount(user=user, source_site=source_site, 
                    openid=openid, access_token=access_token, expires_in=expires_in
                )
                open_acct.save()
                
                Family.objects.create_default(user) #为新注册用户创建默认家庭
            except Exception, e:
                logs.error(e)
                return http.failed('账号绑定异常')
            
            ##############   结束注册并绑定处理    ###############
            
        except Exception, e:
            logs.error(e)
            return http.failed('The3rd login Exception')
 
        user.post_login(req)
        family = user.get_family
        if not family: # 不返回用户与家庭相关的信息
            response = serialize_response(user)
        else:    
            response = serialize_response(family.get_member(user.id))
        
        token = user.get_authtoken()
        if not token:
            return http.resp('authtoken_error', 'Auth error')
        response.set_cookie('authtoken', token)
        
        return response
     
 
# def _get_token(source_site, code):
#     '''以字典形式返回access_token, expires_in'''
#     result = {}
#     if source_site == 'qq':
#         params = urllib.urlencode({
#             'client_id': settings.QQ_APP_KEY,
#             'client_secret': settings.QQ_APP_SECRET,
#             'code': code,
#             'redirect_uri': settings.QQ_CALLBACK_URL
#         })
#         getTokenUrl = settings.QQ_GET_TOKEN_URL + params
#         logs.info(getTokenUrl)
#         req = urllib2.Request(getTokenUrl)
#         resp = urllib2.urlopen(req)
#         content = resp.read()
#         access_token = urllib2.urlparse.parse_qs(content).get('access_token', [''])[0]
#         result.update({'access_token':access_token})
#         logs.info("Token got: %s" % result)
#          
#     return result
#  
# def _get_openid(data):
#     req = urllib2.Request('%s?access_token=%s' % (settings.QQ_GET_OPENID_URL, data["access_token"]))
#     resp = urllib2.urlopen(req)
#     content = resp.read()
#     content = content[content.find('(') + 1:content.rfind(')')]
#     openid = simplejson.loads(content).get('openid')
#     logs.info("Openid got: %s" % openid)
#     return openid
#  
# def _get_userinfo(source_site, access_token, openid):
#     '''Get user info from the3rd site'''
#     req2 = urllib.urlopen("%s?access_token=%s&oauth_consumer_key=%s&openid=%s&format=json" % \
#         (settings.QQ_GET_USERINFO_URL, access_token, settings.QQ_APP_KEY, openid))
#     return simplejson.loads(req2.read())
